home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d22 / adjram41.arc / ADJRAM.C next >
C/C++ Source or Header  |  1988-05-14  |  53KB  |  1,509 lines

  1. /*      Adjustable Ram Disk
  2.  
  3.  (c)    Copyright 1986,1987 by Gary Cramblitt.  All Rights Reserved.
  4.  
  5.  
  6.  v2.2    1 Jul 86   Initial version
  7.  v2.3   24 Aug 86   Bug.  FAT media byte not updated properly.
  8.  v2.4   29 Aug 86   If current drive is memory disk drive, reset current
  9.                     directory on exit.
  10.  v2.5   30 Aug 86   Increase FAT to permit max size of 2043K;
  11.                     Increase size of root directory to 128 entries;
  12.                     Start code for /E option (Expanded Memory Support)
  13.  v3.0   30 Aug 86   Finish code for /E option.
  14.  v3.1    2 Oct 86   Fix shrink bug.  Not packing subdirectories properly
  15.  v3.2   18 Oct 86   Allow SIZE= clause for AMDISK.  Also allow drive letters
  16.                     A through L.
  17.  v4.0    3 Jan 87   Add reserved conventional memory support as
  18.                       R:xxxx:nn option.
  19.                     Full support for expanded memory mixed with conventional
  20.                       memory.
  21.                     Max size of memory disk based upon available memory and
  22.                       cluster size.  
  23.                     User requested expansion or shrinkage automatically
  24.                       adjusted to within limits and rounded to nearest
  25.                       memory block boundary.
  26.                     In expanded memory, use larger block size (64K) to avoid
  27.                       excessive usage of EMM handles.
  28.                     Display Memory Block Table only if /t option specified.
  29.                     Bug.  Stop processing directory when first "never_used"
  30.                       flag is encountered.
  31.                     Bug in DeSmet free() routine.  Causes memory overflo.
  32.                       Calculate stack space manually instead.
  33.                     Restrict program size to under 32K.
  34.                     Clean up error reporting.  Tell user if error has
  35.                       corrupted the memory disk.
  36. v4.1    15 May 88   Restrict reserved memory to above segment a000.
  37.                     Initialize reserved memory when block is allocated.
  38.  
  39.         For program usage, see the last routine.
  40.  
  41.         A few definitions:
  42.                 1.  CONVENTIONAL MEMORY is that memory directly addressable
  43.                     by the 8088 or 8086 CPU and controlled by DOS.  On the
  44.                     IBM PC, it falls below 640K.
  45.                 2.  RESERVED MEMORY is also directly addressable by the
  46.                     8088 or 8086, but it is not controlled by DOS.  On
  47.                     the IBM PC, it falls above 640K and below 1MB.  It
  48.                     is usually reserved for use by the PC's BIOS.  If your
  49.                     machine has memory mapped to addresses between 640K and
  50.                     1MB and that memory is not needed for any other purpose,
  51.                     and it is directly addressable by the CPU (no special
  52.                     "paging" or "mapping" required to access it), then
  53.                     ADJRAM can use it via the R option.  See .DOC
  54.                 3.  EXTENDED MEMORY is addressed above 1MB.  It is only
  55.                     available on AT type machines (80286 or 80386 CPUs).
  56.                     It is not currently supported by ADJRAM.
  57.                 4.  EXPANDED MEMORY is memory conforming to the Lotus/
  58.                     Intel/Microsoft Expanded Memory Specification.  It
  59.                     is supported by ADJRAM if you have the EMM loaded.
  60.  
  61.         This program is coded in DeSmet C v2.4.  Any function beginning
  62. with underscore (_) is a non-standard routine from the DeSmet library.
  63. They are:
  64.         _showcs()
  65.                 Synopsis: unsigned _showcs()
  66.  
  67.                 Returns the current value of CS.
  68.  
  69.         _showds()
  70.                 Synopsis: unsigned _showds()
  71.  
  72.                 Returns the current value of DS (= SS in DeSmet C).
  73.  
  74.         _showsp()
  75.                 Synopsis: char *_showsp()
  76.  
  77.                 Returns the current value of SP.
  78.  
  79.         _setsp()
  80.                 Synopsis: void _setsp(newStackValue);
  81.                           unsigned newStackValue;
  82.  
  83.                 Sets the SP register to the specified value.
  84.  
  85.         _memory()
  86.                 Synopsis: char *_memory()
  87.  
  88.                 Returns a pointer to the first byte of free memory past
  89.                 the end of initialized memory (global data).
  90.  
  91.         _peek()
  92.                 Synopsis: char _peek(offset,segment)
  93.                           char *offset;
  94.                           unsigned segment;
  95.                 Returns the byte at specified far address.
  96.  
  97.         _doint()
  98.                 Synopsis: set any or all of the externs
  99.                           _rax,_rbx,_rcx,_rdx,_rsi,_rdi,_res,_rds
  100.                 followed by
  101.                           _doint(interrupt number);
  102.                 Performs the specified interrupt with the specified
  103.                 registers set from the externs.
  104.  
  105.                 After the call, _rax, etc. can be used.  _carryf
  106.                 and _zerof are extern char variables set to 1 if
  107.                 the carry or zero flag is set.
  108.  
  109. In addition, the following routines are semi_standard, and may have
  110. slightly different implementations with your compiler:
  111.  
  112.         strncmp()
  113.                 Synopsis: char *strncmp(leftstring,rightstring,max)
  114.                           char *leftstring, *rightstring
  115.                           int max               
  116.                 strncmp() compares up to a specified number of chars
  117.                 in two strings.  It returns 0 if the specified number
  118.                 of characters in the string are the same.
  119.  
  120. */
  121.  
  122. /* ==== Definitions ==== */
  123.  
  124. /* ---- Overall Definitions ---- */
  125.  
  126. #define true                    1
  127. #define false                   0
  128.  
  129. /*
  130.    ---- To compile without LOTUS/INTEL/Microsoft Expanded Memory
  131.         support, change the "1" to "0" in the next statement.  Will
  132.         save about 1500 bytes in EXE file.
  133. */
  134.  
  135. #define em_support              1       /* compile for EM support */
  136.  
  137. /* ---- To compile without reserved memory support, change
  138.         the "1" to "0" in the next statement.
  139. */
  140.  
  141. #define rm_support              1       /* compile for reserved memory */
  142.  
  143. #define debug                   0       /* compile with debug stmts */
  144.  
  145. /* ---- Other customizable symbols */
  146.  
  147. #define start_rm_addr        0xa000    /* start of reserved memory */
  148.  
  149. /*
  150.         The following symbols must correspond to the same symbols in
  151.         file amdisk.asm.  If one is changed, so must the other.
  152.         This is because the first memory block may not be
  153.         deallocated.
  154. */
  155.  
  156. #define def_size_K              64      /* default to 64K RAM disk */
  157. #define min_size_K              (boot_sec.mem_blk_table[0].siz/sec_per_K)
  158. #define min_size_sec            boot_sec.mem_blk_table[0].siz
  159. #define em_sec_per_blk          512     /* increment in 256K Expanded   */
  160.                                         /*   Memory blocks              */
  161. #define cnv_sec_per_blk         64      /* increment in 32K conventional*/
  162.                                         /*   memory blocks              */
  163.  
  164. /*
  165.    ---- Disk definitions.  Note: These constants should be made into
  166.         variables or functions if this program's algorithms need to
  167.         be generalized to disks of any type, especially high density disks.
  168.         Since this program works only in conjunction with amdisk.asm,
  169.         it is OK to make them constants here.
  170. */
  171.  
  172. #define bytes_per_sec           512     /* 512 bytes per sector */
  173. #define par_per_sec             (512/16)
  174.                                         /* 32 paragraphs per sector */
  175. #define sec_per_K               (1024/512)
  176.                                         /* sectors per 1024 bytes */
  177. #define dir_per_sec             (512/sizeof(struct dir_entry))
  178.                                         /* directory entries per sector */
  179. #define K_per_blk               (sec_per_blk/sec_per_K)
  180.                                         /* K per memory block */
  181. #define em_pag_per_blk          (K_per_blk/16)
  182.                                         /* 16K EM pages per mem block */
  183. #define max_clusters            4086    /* max clusters per disk
  184.                                            (12-bit FAT entries) */
  185. #define max_mem_blks            (max_clusters/cnv_sec_per_blk)
  186.                                         /* Maximum number of memory blocks */
  187.                                         /*   assuming cluster size of 1    */
  188. #define bytes_per_fat           (max_clusters*3/2)
  189. #define sec_per_fat             ((bytes_per_fat+bytes_per_sec-1)/bytes_per_sec)
  190. #define fat_size                (sec_per_fat*bytes_per_sec)
  191.  
  192. /* ---- Program Segment Prefix ---- */
  193.  
  194. #define environment_segment     0x2c    /* segment address of the environment */
  195.  
  196. /* ---- DOS interrupts ---- */
  197.  
  198. #define dosi_dosf               0x21    /* DOS function interrupt */
  199. #define dosi_dsk_read           0x25    /* DOS absolute disk read interrupt */
  200. #define dosi_dsk_write          0x26    /* DOS absolute disk write interrupt */
  201.  
  202. /* ---- User interrupts ---- */
  203.  
  204. #define usri_emm                0x67    /* LOTUS/INTEL/Microsoft Expanded
  205.                                            Memory Manager */
  206. /* ---- DOS functions ---- */
  207.  
  208. #define dosf_seldisk            0x0e    /* set default disk */
  209. #define dosf_getdisk            0x19    /* get current default disk */
  210. #define dosf_getver             0x30    /* get DOS version number */
  211. #define dosf_keepprc            0x31    /* keep process (term and stay resident) */
  212. #define dosf_drvfre             0x36    /* get disk free space */
  213. #define dosf_chdir              0x3b    /* set default directory */
  214. #define dosf_openh              0x3d    /* open file handle */
  215. #define dosf_closeh             0x3e    /* close file handle */
  216. #define dosf_ioctl              0x44    /* IOCTL */
  217. #define dosf_cwd                0x47    /* get current directory */
  218. #define dosf_alloc              0x48    /* allocate memory block */
  219. #define dosf_dealloc            0x49    /* deallocate memory block */
  220. #define dosf_setblk             0x4a    /* modify memory block */
  221.  
  222. /* ---- LOTUS/INTEL/Microsoft Expanded Memory Manager functions ---- */
  223.  
  224. #define emm_status              0x40    /* get manager status */
  225. #define emm_get_PFseg           0x41    /* get page frame segment */
  226. #define emm_get_pages           0x42    /* get number of pages */
  227. #define emm_get_handle          0x43    /* get handle and allocate memory */
  228. #define emm_map_memory          0x44    /* map memory */
  229. #define emm_fre_handle          0x45    /* free handle and memory */
  230. #define emm_get_ver             0x46    /* get EMM version */
  231. #define emm_sav_map             0x47    /* save mapping context */
  232. #define emm_res_map             0x48    /* restore mapping context */
  233. #define emm_num_handles         0x4b    /* get number of EMM handles */
  234. #define emm_hdl_pages           0x4c    /* get pages owned by handle */
  235. #define emm_all_pages           0x4d    /* get pages for all handles */
  236. #define emm_pag_map             0x4e    /* get or set page map */
  237.  
  238. #define nor_flg                 0       /* memory block is in normal memory */
  239. #define em_flg                  1       /* memory block is in expanded memory */
  240. #define rm_flg                  2       /* memory block is in reserved memory */
  241.  
  242. /*
  243.    ---- DOS Errors ----
  244.         These codes are returned by program and can be tested with DOS
  245.         IF statement.
  246. */
  247.  
  248. #define dose_noerr              0       /* no error */
  249. #define dose_invfunc            1       /* invalid function */
  250. #define dose_too_many_dir       4       /* too many directories */
  251. #define dose_arena              7       /* arena trashed */
  252. #define dose_noram              8       /* not enough memory */
  253. #define dose_inv_env            10      /* invalid environment */
  254. #define dose_invdrv             15      /* invalid drive */
  255.  
  256. /* ---- Directory definitions ---- */
  257.  
  258. #define never_used              0       /* directory entry never used */
  259. #define erased                  0xe5    /* file has been erased */
  260.  
  261. /* ---- Directory Attribute Byte ---- */
  262.  
  263. #define RO_bit                  0x01    /* this bit indicates read-only */
  264. #define hidn_bit                0x02    /* this bit indicates hidden file */
  265. #define sys_bit                 0x04    /* this bit indicates system file */
  266. #define vol_bit                 0x08    /* this bit indicates volume label */
  267. #define dir_bit                 0x10    /* this bit indicates subdirectory */
  268. #define arc_bit                 0x20    /* this bit indicates modified file */
  269.  
  270. /* ---- FAT definitions ---- */
  271.  
  272. #define available               0       /* cluster is available for use */
  273. #define bad                     0xff7   /* cluster is bad */
  274. #define last_low                0xff8   /* last cluster for the file */
  275. #define last_high               0xfff   /* last cluster for the file */
  276.  
  277. extern unsigned _rax, _rbx, _rcx, _rdx, _rsi, _rdi, _res, _rds;
  278. extern char     _carryf, _zerof;
  279.  
  280. /* ---- Directory entry ---- */
  281.  
  282. struct dir_entry {
  283.   union dir_name {
  284.     char                name[8];
  285.     unsigned char       status;
  286.   } u_name;
  287.   char          ext[3];
  288.   unsigned char attr;
  289.   unsigned char reserved[10];
  290.   unsigned int  time;
  291.   unsigned int  date;
  292.   unsigned int  first_cluster;
  293.   unsigned long size;
  294. };
  295.  
  296. /* ==== Global Data Storage ==== */
  297.  
  298. unsigned int pgm_seg;           /* CS at start of program saved here */
  299. int     drive_number;           /* memory disk drive number A=0, B=1, etc */
  300. int     mdisk_size_K;           /* memory disk size in K */
  301. int     user_chg;               /* user's desired change in sectors */
  302. int     mdisk_chg;              /* the difference between disk's current size
  303.                                    and the final size (in sectors) */
  304. int     free_secs;              /* unused sectors in the memory disk */
  305. int     first_dir_sector;
  306. int     last_dir_sector;        /* loc of directory */
  307. int     first_fat_sector;
  308. int     last_fat_sector;        /* loc of FAT */
  309. int     free_cluster;           /* ptr to first free cluster */
  310. int     first_data_sector;      /* first sector after the last FAT */
  311. int     avail_mem_blks;         /* max expansion size in memory blocks */
  312. int     sec_per_blk;            /* sectors per memory block */
  313. int    gbl_argc;        /* command line argument count */
  314. char    *gbl_argv;            /* command line argument vector table */
  315. unsigned char expansion_type;   /* 0 = expand using conventional memory */
  316.                                 /* 1 = expand using expanded memory     */
  317.                                 /* 2 = expand using reserved memory     */
  318. unsigned int user_rm_addr;      /* user-specified reserved memory       */
  319.                                 /*   starting paragraph address         */
  320.  
  321. /* ---- Memory Disk Boot Record ---- */
  322.  
  323. struct  boot_record {
  324.   unsigned char jmp[3];   /* non-bootable (no jump instruction ) */
  325.   char    ident[8];       /* identification */
  326.   unsigned int  bytes_in_sector;/* bytes/sector */
  327.   unsigned char sec_per_cluster;/* sectors/cluster */
  328.   unsigned int  bpb_reserved;     /* reserved sectors */
  329.   unsigned char bpb_fats;         /* number of FAT's */
  330.   unsigned int  bpb_root;         /* directory entries in root */
  331.   unsigned int  bpb_total;        /* total number of sectors */
  332.   unsigned char bpb_media;        /* media byte = number of mem blocks */
  333.   unsigned int  bpb_fat_size;     /* sectors/FAT */
  334.  
  335.   unsigned int  sec_per_track;    /* sectors/track */
  336.   unsigned int  heads;            /* number of heads */
  337.   unsigned int  hidden;           /* hidden sectors */
  338.  
  339.   struct mem_blk_table_entry {
  340.     unsigned char typ;            /* type of blk: 0 = normal 1 = EM */
  341.     unsigned int par;             /* paragraph address of block */
  342.     unsigned int siz;             /* number of sectors in the memory block */
  343.     unsigned int hdl;             /* expanded memory handle */
  344.   } mem_blk_table[max_mem_blks];
  345.   char  rest_of_record[bytes_per_sec - sizeof(struct boot_record)];
  346. } boot_sec;
  347.  
  348. /* ---- Memory disk default pathname ----- */
  349.  
  350.   struct pathname {
  351.     char drv;
  352.     char colon;
  353.     char slash;
  354.     char dir[64];
  355.   } mdisk_pathname; /* Ram disk's current pathname */
  356.  
  357. /* ---- Default drive ---- */
  358.  
  359.   unsigned char default_drive;
  360.  
  361. /* ---- Expanded Memory Manager ----- */
  362.  
  363.   unsigned int em_PFseg;        /* EMM's page frame segment */
  364.   char em_device_name[] = "EMMXXXX0"; /* EMM device name */
  365.  
  366. /* ---- Declare types of library functions which return non-integer values */
  367.  
  368. char *strncmp();        /* compares 2 strings of specified length */
  369. unsigned _showcs();     /* returns current CS register */
  370. unsigned _showds();     /* returns current DS = SS register */
  371. char *_showsp();        /* returns current SP register */
  372. void _setsp();          /* sets SP to specified value */
  373. char *_memory();        /* returns pointer to end of initialized memory */
  374. char _peek();           /* returns a byte given segment and offset */
  375.  
  376. /* ==== MAIN ROUTINE ==== */
  377.  
  378. main(argc, argv)
  379.   int argc;
  380.   char *argv[];
  381. {
  382.   /*
  383.      ---- Begin by saving the CS, which points to just after the 256-byte
  384.           PSP.  By subtracting 16, we get the segment pointer for the
  385.           program segment. This is, of course, highly DeSmet C dependent
  386.           code.  Hopefully, your compiler has some mechanism for getting
  387.           the program segment pointer.
  388.   */
  389.  
  390.   pgm_seg = _showcs() - 16;
  391.  
  392.   gbl_argc = argc;
  393.   gbl_argv = argv;
  394.  
  395.   /* ---- Now move the stack down below 32K, so that we don't clobber
  396.           DOS memory arena blocks we will be creating if expanding.
  397.           New stack pointer is computed so that sum of code, global
  398.           variables, and stack is less than 32K.  DeSmet run-time memory
  399.           looks like this:
  400.  
  401.              pgm_seg --> program segment prefix (256 bytes)
  402.                   CS --> code
  403.                DS,SS --> initialized data
  404.                          uninitialized data
  405.            _memory() --> (high water)
  406.                          stack
  407.                   SP -->
  408.   */
  409.  
  410.   if(_showsp() < 
  411.     (((pgm_seg + (cnv_sec_per_blk*par_per_sec) - _showds()) * 16) - 0x80)) {
  412.     printf("Error -- ADJRAM requires 32K of free memory to run\n");
  413.     adjram_exit(dose_noram);
  414.   } else 
  415.     _setsp(((pgm_seg + (cnv_sec_per_blk*par_per_sec) - _showds()) * 16) - 0x80);
  416.  
  417. #if debug
  418.   printf("CS = %4x  next block = %4x\n",pgm_seg,
  419.     pgm_seg + (cnv_sec_per_blk*par_per_sec));
  420.   printf("DS = %4x  end of dat = %4x\n",_showds(),
  421.     _showds() + ((_memory()+15)/16));
  422.   printf("mem= %4x\n",_memory());
  423.   printf("SP = %4x  top of stk = %4x\n",_showsp(),
  424.     _showds() + (_showsp()/16)) + 0x8;
  425. #endif
  426.  
  427.  /* ---- Now call the real main routine.  Never comes back from call. */
  428.  
  429.   actual_main(gbl_argc, gbl_argv);
  430. }
  431.  
  432. /* ==== ACTUAL_MAIN ROUTINE ==== */
  433.  
  434. int actual_main(argc, argv)
  435.   int argc;
  436.   char *argv[];
  437.  
  438. {
  439.   int   err_code;               /* exit error code ( 0 if no error ) */
  440.   int    non_fatal_err;        /* user errors not requiring reboot */
  441.   int   j;
  442.   int   k;
  443.   int   m;
  444.  
  445.   /* ---- File Allocation Table ----*/
  446.  
  447.   unsigned char fat[fat_size];
  448.  
  449.   printf("Adjustable RAM Disk v4.1    ");
  450.   printf(" (c) Copyright 1986, 1987, 1988 by Gary Cramblitt\n");
  451. #if em_support
  452.   printf("(-- Expanded Memory Version)\n");
  453. #endif
  454.  
  455.   printf("\n");
  456.   err_code = dose_noerr;  /* set no error */
  457.   non_fatal_err = dose_noerr;
  458.  
  459.   /* ---- Make sure MS-DOS 2 or above ---- */
  460.  
  461.   _rax = dosf_getver << 8;
  462.   _doint(dosi_dosf);
  463.   if (_rax & 0x00ff < 2) {
  464.     printf ("Error -- this program requires DOS version 2 or above.\n");
  465.     adjram_exit(dose_invfunc);
  466.   }
  467.  
  468.   /* ---- If user wants to expand into LIM Expanded Memory or reserved
  469.           memory, set the memory block size appropriately.  If user is
  470.           expanding into reserved memory, get the paragraph addresses
  471.           and verify.
  472.   */
  473.  
  474.   expansion_type = nor_flg;
  475.   sec_per_blk = cnv_sec_per_blk;
  476. #if em_support
  477.   for (j = 1; j < argc; j++) if (toupper(*(argv[j]+1)) == 'E')
  478.     expansion_type = em_flg;
  479.   if (expansion_type == em_flg) sec_per_blk = em_sec_per_blk;
  480. #endif
  481. #if rm_support
  482.   for (j = 1; j < argc; j++) if (toupper(*argv[j]) == 'R') {
  483.     expansion_type = rm_flg;
  484.     user_rm_addr = htoi(argv[j]+2);
  485.     j = atoi(argv[j]+7);
  486.     if (j > 64 || j < 1 || user_rm_addr < start_rm_addr) {
  487.       printf("Error -- Bad reserved memory parameter.\n");
  488.       printf("         Must be in form R:hhhh:dd.  Check documentation.\n");
  489.       adjram_exit(dose_invfunc);
  490.     }
  491.     sec_per_blk = j * sec_per_K;
  492.  
  493.     /* --- Check that specified reserved memory is really there. --- */
  494.  
  495.     _poke(0xed, 0, user_rm_addr);
  496.     _poke(0xed, (sec_per_blk*bytes_per_sec)-1, user_rm_addr);
  497.     if (   (_peek(0, user_rm_addr) != 0xed)
  498.         || (_peek((sec_per_blk*bytes_per_sec) - 1, user_rm_addr) != 0xed)) {
  499.       printf("Error -- could not locate reserved memory block ");
  500.       printf("from %4x:0000 to %4x:%4x\n",
  501.         user_rm_addr, user_rm_addr, (sec_per_blk*bytes_per_sec)-1);
  502.       printf("         Probably caused by no such memory.\n");
  503.       adjram_exit(dose_arena);
  504.     }
  505.   }
  506. #endif
  507.  
  508.   /*
  509.      ---- If using Expanded Memory, check to make sure that
  510.           EMM is available.  Check is made by opening the EMM device.
  511.           If open fails, EMM is not loaded.  Get page frame segment from
  512.           EMM.  Raw DOS handle open is used here to avoid bringing in
  513.           huge DeSmet IO routines.
  514.   */
  515.  
  516. #if em_support
  517.   if (expansion_type == em_flg) {
  518.     _rax = (dosf_openh << 8) + 0;       /* open a handle function */
  519.     _rds = _showds();
  520.     _rdx = &em_device_name;             /* DS:DX => "EMMXXXX0" */
  521.     _doint(dosi_dosf);
  522.     j = _rax;                           /* j = returned handle */
  523.     if (_carryf == 1) {
  524.       printf("Error -- Expanded Memory Manager is not available.\n");
  525.       printf("         Error code: %d\n",_rax);
  526.       adjram_exit(dose_invfunc);
  527.     } else {
  528.       _rax = (dosf_ioctl << 8) + 7;     /* Get output status */
  529.       _rbx = j;                         /* BX = handle */
  530.       _doint(dosi_dosf);
  531.       _rbx = j;                         /* BX = handle */
  532.       j = _rax & 0xff;                  /* j = returned device status */
  533.       _rax = dosf_closeh << 8;          /* close handle */
  534.       _doint(dosi_dosf);
  535.       if (j == 0xff) {
  536.         _rax = emm_get_PFseg << 8;      /* get EM page frame segment */
  537.         _doint(usri_emm);
  538.         _rax = _rax >> 8;               /* status in AH */
  539.         if (_rax == 0) em_PFseg = _rbx;
  540.         else {
  541.           printf("Error -- Expanded Memory Manager could not report page frame.\n");
  542.           printf("         Error code: %2xH\n",_rax);
  543.           adjram_exit(dose_noram);
  544.         }
  545.       } else {
  546.         printf("Error -- Expanded Memory Manager is not available.\n");
  547.         printf("         Error code: %2xH\n",j);
  548.         adjram_exit(dose_invfunc);
  549.       }
  550.     }
  551.   }
  552. #endif
  553.  
  554.   /*
  555.      ---- Release our own environment block.  It doesn't hurt to do so.
  556.           Done so we don't have to do it in the future when user requests
  557.           memory disk shrinkage.
  558.   */
  559.  
  560.   _res = peekw(environment_segment, pgm_seg);
  561.   _rax = dosf_dealloc << 8;
  562.   _doint(dosi_dosf);  
  563.   if (_carryf == 1) {
  564.     printf("Error -- could not free environment block.  Error code %d\n",
  565.       _rax);
  566.     adjram_exit(dose_inv_env);
  567.   }
  568.  
  569.   /*
  570.      ---- Set default minimum size of RAM disk.  Will be overwritten as
  571.           soon as boot sector is read in.  Set here so that help message
  572.           will have something to display.
  573.   */
  574.  
  575.   min_size_sec = def_size_K*sec_per_K;
  576.  
  577.   /*
  578.      ---- Parse the command line drive letter.
  579.           If user makes an error, give him usage message and exit.
  580.   */
  581.  
  582.   if (argc < 2 || 
  583.     (drive_number = (toupper(*argv[1]) - 65)) < 0 || drive_number > 11) {
  584.     dsp_usage();
  585.     exit(dose_invfunc);
  586.   }
  587.  
  588.   /*
  589.      ---- Read the boot sector from the memory disk.  Verify that we are
  590.           dealing with the correct memory disk.
  591.   */
  592.  
  593.   if ((err_code = readsec(drive_number, 0, &boot_sec)) != dose_noerr) {
  594.     printf("Error -- could not read boot sector.  Error code %1d\n", err_code);
  595.     adjram_exit(dose_invdrv);
  596.   }
  597.   if (strcmp(boot_sec.ident,"AMDISK4 ") != 0) {
  598.     printf("Error -- that drive is not the adjustable RAM disk or invalid\n");
  599.     printf("         version of AMDISK for this version of ADJRAM!\n");
  600.     adjram_exit(dose_invdrv);
  601.   }
  602.  
  603. #if debug
  604.   if (fat_size != boot_sec.bpb_fat_size*bytes_per_sec)
  605.     printf("Coding Error -- FAT_SIZE does not match BPB_FAT_SIZE\n");
  606. #endif
  607.  
  608.   /* ---- Determine the drive's current size and free space. */
  609.  
  610.   mdisk_size_K = boot_sec.bpb_total/sec_per_K;
  611.   _rax = dosf_drvfre << 8;
  612.   _rdx = drive_number + 1;
  613.   _doint(dosi_dosf);
  614.   if (_rax == 0xffff) {
  615.     printf("Error -- Invalid drive letter or RAM disk is not loaded.\n");
  616.     adjram_exit(dose_invdrv);
  617.   }
  618.   free_secs = _rbx * _rax;      /* avail clusters * sectors per cluster */
  619.   printf("Drive %c: Starting Size: %dK       Free Space: %dK\n",
  620.     drive_number + 65, mdisk_size_K, free_secs/sec_per_K);
  621.  
  622.   /*  ---- Obtain the default disk. ---- */
  623.  
  624.   _rax = dosf_getdisk << 8;
  625.   _doint(dosi_dosf);
  626.   default_drive = _rax & 0x00ff;
  627.   
  628.   /* ---- Get the memory disk's current directory. ---- */
  629.  
  630.   mdisk_pathname.drv = drive_number + 'A';
  631.   mdisk_pathname.colon = ':';
  632.   mdisk_pathname.slash = '\\';
  633.   _rax = dosf_cwd << 8;
  634.   _rdx = drive_number + 1;
  635.   _rds = _showds();
  636.   _rsi = mdisk_pathname.dir;
  637.   _doint(dosi_dosf);
  638.  
  639.   /*
  640.      ---- Read in the FAT.
  641.   */
  642.  
  643.   first_fat_sector = boot_sec.bpb_reserved;
  644.   last_fat_sector = first_fat_sector + boot_sec.bpb_fat_size - 1;
  645.   k = 0;
  646.   for (j = first_fat_sector; j <= last_fat_sector; j++) {
  647.     readsec(drive_number, j, fat + k);
  648.     k = k + bytes_per_sec;
  649.   }
  650.  
  651.   /*
  652.      ---- Determine what user wants to do -- expand or shrink the memory
  653.           disk, and by how much.
  654.   */
  655.  
  656.   user_chg = 0;
  657.   if (argc >= 3) {
  658.     if (expansion_type == rm_flg) user_chg = sec_per_blk/sec_per_K;
  659.     else if (toupper(*argv[2]) == 'F')
  660.       user_chg = atoi(argv[2]+1) - free_secs/sec_per_K;
  661.     else if (toupper(*argv[2]) == 'M') {
  662.       user_chg = atoi(argv[2]+1) - free_secs/sec_per_K;
  663.       if (user_chg < 0) user_chg = 0;
  664.     } else if (*argv[2] != '/') {
  665.       user_chg = atoi(argv[2]);
  666.       if (*argv[2] == '+' || *argv[2] == '-'); else
  667.         user_chg = user_chg - (boot_sec.bpb_total/sec_per_K);
  668.     }
  669.   }
  670.   user_chg = user_chg * sec_per_K;
  671.  
  672.   /*
  673.     ---- Display minimum shrinkage size (if shrink is possible at all.
  674.   */
  675.  
  676.   if (user_chg <= 0) {
  677.     if (boot_sec.bpb_media > 1)
  678.       printf("     Next Smaller Size: %dK    Min Shrinkage: %dK\n",
  679.         (boot_sec.bpb_total -
  680.          boot_sec.mem_blk_table[boot_sec.bpb_media-1].siz)/sec_per_K,
  681.         boot_sec.mem_blk_table[boot_sec.bpb_media-1].siz/sec_per_K);
  682.     else
  683.       printf("RAM disk is as small as is possible.\n");
  684.   }
  685.  
  686.   /*
  687.      ---- Size our program to 32K if user is expanding the memory
  688.           disk.  This 32K program segment will become the first new
  689.           memory block added to the memory disk's Memory Block Table.
  690.   */
  691.  
  692.   if (user_chg >=0) {
  693.     if (expansion_type == nor_flg) {
  694.       _rax = dosf_setblk << 8;
  695.       _res = pgm_seg;
  696.       _rbx = sec_per_blk * par_per_sec;
  697.       _doint(dosi_dosf);
  698.       if (_carryf == 1) {
  699.         printf("Error -- could not modify memory block size.  Error code %1d\n",
  700.           _rax);
  701.         printf("         Probably caused by not enough free memory.\n");
  702.         adjram_exit(dose_noram);
  703.       }
  704.     }
  705.   }
  706.  
  707.   /*
  708.      ---- Determine maximum expansion in terms of memory blocks.
  709.           For expanded memory, ask EMM how many pages are available.
  710.           Size available conventional memory in units of 32K memory
  711.           blocks.  Always leave at least 32K of memory so that ADJRAM
  712.           has room to be run again.
  713.           For reserved memory, user specifies exactly one block and he
  714.           tells how big that block is.
  715.           In any case, maximum expansion is limited to available
  716.           clusters.
  717.   */
  718.  
  719.   if (user_chg >= 0) {
  720. #if em_support
  721.     if (expansion_type == em_flg) {
  722.       _rax = emm_get_pages << 8;        /* get number of EM pages */
  723.       _doint(usri_emm);
  724.       avail_mem_blks = _rbx/em_pag_per_blk;
  725.     }
  726. #endif
  727.     if (expansion_type == nor_flg) {
  728.       _carryf == 0;
  729.       for (avail_mem_blks = 0; _carryf == 0; avail_mem_blks++) {
  730.         _rax = dosf_alloc << 8;
  731.         _rbx = sec_per_blk * par_per_sec;
  732.         _doint(dosi_dosf);
  733.         boot_sec.mem_blk_table[boot_sec.bpb_media + avail_mem_blks].par = _rax;
  734.       }
  735.       avail_mem_blks = avail_mem_blks - 1;
  736.       for (j = avail_mem_blks; j > 0; j--) {
  737.         _rax = dosf_dealloc << 8;
  738.         _res = boot_sec.mem_blk_table[boot_sec.bpb_media + j - 1].par;
  739.         _doint(dosi_dosf);
  740.       }
  741.     }
  742. #if rm_support
  743.     if (expansion_type == rm_flg) avail_mem_blks = 1;
  744. #endif
  745.     j = ((max_clusters * boot_sec.sec_per_cluster) -
  746.           boot_sec.bpb_total)/ sec_per_blk;
  747.     if (j < avail_mem_blks) avail_mem_blks = j;
  748.     printf("     Max Possible Size: %dK    Max Expansion: %dK\n",
  749.       (avail_mem_blks*K_per_blk) + (boot_sec.bpb_total/sec_per_K),
  750.       avail_mem_blks*K_per_blk);
  751.   }
  752.  
  753.   /*
  754.     ---- Round user's request to the nearest memory block boundary.
  755.          If expanding, round up, but do not exceed available memory.
  756.          If shrinking, round up (less negative), and do not exceed
  757.          free sectors.
  758.   */
  759.  
  760.   mdisk_chg = user_chg;
  761.   if (mdisk_chg > 0) {
  762.     mdisk_chg = ((mdisk_chg + sec_per_blk - 1)/sec_per_blk) * sec_per_blk;
  763.     if (mdisk_chg/sec_per_blk > avail_mem_blks) {
  764.       mdisk_chg = avail_mem_blks * sec_per_blk;
  765.       non_fatal_err = dose_noram;
  766.     }
  767.   } else if (mdisk_chg < 0) {
  768.     if (-mdisk_chg > free_secs) {
  769.       printf("Warning -- Insufficient free space in disk to shrink that much.\n");
  770.       printf("           You must erase some files.\n");
  771.       mdisk_chg = -free_secs;
  772.       non_fatal_err = dose_noram;
  773.     }
  774.     for (j = boot_sec.bpb_media-1;
  775.       -mdisk_chg >= boot_sec.mem_blk_table[j].siz && j > 0; j--) {
  776.       mdisk_chg = mdisk_chg + boot_sec.mem_blk_table[j].siz;
  777.     }
  778.     mdisk_chg = 0;
  779.     for (j++; j < boot_sec.bpb_media; j++)
  780.       mdisk_chg = mdisk_chg - boot_sec.mem_blk_table[j].siz;
  781.   }
  782.   if (user_chg != mdisk_chg)
  783.     printf("Warning -- Rounding your requested change of %dK to %dK.\n",
  784.            user_chg/sec_per_K,mdisk_chg/sec_per_K);
  785.  
  786.   mdisk_size_K = (mdisk_chg + boot_sec.bpb_total)/sec_per_K;
  787.   printf("            Final Size: %dK    Amount Change: %dK\n",
  788.     mdisk_size_K, mdisk_chg/sec_per_K);
  789.  
  790.   /*
  791.     ---- From here on, any error that occurs would probably require the
  792.          user to reboot.
  793.   */
  794.  
  795.   /*
  796.      ---- Expand or shrink as indicated.
  797.   */
  798.  
  799.   if (mdisk_chg < 0 ) err_code = shrink(fat);
  800.   else if (mdisk_chg > 0) err_code = expand();
  801.  
  802.   /* ---- Write the new FAT back to disk */
  803.  
  804.   for (m = 0; m < boot_sec.bpb_fats; m++) {
  805.     k = 0;
  806.     for (j = first_fat_sector; j <= last_fat_sector; j++) {
  807.       writesec(drive_number, j, fat + k);
  808.       k = k + bytes_per_sec;
  809.     }
  810.     first_fat_sector = first_fat_sector + boot_sec.bpb_fat_size;
  811.     last_fat_sector = last_fat_sector + boot_sec.bpb_fat_size;
  812.   }
  813.  
  814.   /* ---- Write the updated boot sector back to memory disk ---- */
  815.  
  816.   if ((err_code = writesec(drive_number, 0, &boot_sec)) != 0) {
  817.     printf("Error -- could not write boot sector back.  Code: %1d\n", err_code);
  818.     err_code = dose_invdrv;
  819.   }
  820.  
  821.   /*
  822.    ---- Restore the memory disk's default directory, then reset default
  823.         drive.
  824.   */
  825.  
  826.   _rax = dosf_chdir << 8;
  827.   _rds = _showds();
  828.   _rdx = &mdisk_pathname;
  829.   _doint(dosi_dosf);
  830.  
  831.   _rax = dosf_seldisk << 8;
  832.   _rdx = default_drive;
  833.   _doint(dosi_dosf);
  834.  
  835.   /* ---- Display Memory Block Table if user requested it.  ----- */
  836.  
  837.   for (j = 1; j < argc; j++) if (toupper(*(argv[j]+1)) == 'T')
  838.     dsp_mem_blk_table();
  839.  
  840.   /*
  841.      ---- Tell user results.
  842.   */
  843.  
  844.   if (err_code != dose_noerr) {
  845.     printf("*** Sorry, RAM disk left corrupted.  Recommend you check\n");
  846.     printf("    your files, save them if possible, and reboot.\n");
  847.   } else err_code = non_fatal_err;
  848.  
  849.   /*
  850.      ---- If expanding using conventional memory, terminate normally by
  851.           using terminate and stay resident function.
  852.   */
  853.  
  854.   if (expansion_type == nor_flg && mdisk_chg > 0) {
  855.     printf("ADJRAM exiting (code %d)\n",err_code);
  856.     _rax = (dosf_keepprc << 8) + dose_noerr;
  857.     _rdx = sec_per_blk * par_per_sec;
  858.     _doint(dosi_dosf);                        /* PROGRAM TERMINATES HERE */
  859.   }
  860.  
  861.   adjram_exit(err_code);
  862.  
  863. } /* End of main routine */
  864.  
  865. /* ==== EXPAND ROUTINE ==== */
  866. /*      Expands the memory disk by allocating new memory blocks to it. */
  867.  
  868. int expand()
  869. {
  870.   int   result, j;
  871.   int   mdisk_dif;
  872.  
  873.   /*
  874.  
  875.   */
  876.  
  877.   mdisk_dif = mdisk_chg;
  878.   result = dose_noerr;
  879.  
  880.     /*
  881.        The first new memory block is ADJRAM's own program segment, which
  882.        has already been resized to 32K.
  883.        Increment the media byte to show an additional memory block has been
  884.        allocated.  Set the starting paragraph and size (in sectors) for the
  885.        new block in the memory block table.
  886.     */
  887.  
  888.   if (expansion_type == nor_flg) {
  889.     mdisk_dif = mdisk_dif - sec_per_blk;
  890.     boot_sec.mem_blk_table[boot_sec.bpb_media].typ = nor_flg;
  891.     boot_sec.mem_blk_table[boot_sec.bpb_media].par = pgm_seg;
  892.     boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
  893.     boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = 0;
  894.  
  895.     boot_sec.bpb_media = boot_sec.bpb_media + 1;
  896.  
  897.     /* Increase the total number of sectors. */
  898.  
  899.     boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
  900.   }
  901.  
  902.   /* Now allocate new memory blocks for the remaining desired space. */
  903.  
  904.   for (;mdisk_dif > 0; mdisk_dif = mdisk_dif - sec_per_blk) {
  905. #if em_support
  906.     if (expansion_type == em_flg) {
  907.       _rax = emm_get_handle << 8;
  908.       _rbx = em_pag_per_blk;
  909.       _doint(usri_emm);
  910.       _rax = _rax >> 8;
  911.       if (_rax == 0) {
  912.         boot_sec.mem_blk_table[boot_sec.bpb_media].typ = em_flg;
  913.         boot_sec.mem_blk_table[boot_sec.bpb_media].par = em_PFseg;
  914.         boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
  915.         boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = _rdx;
  916.         boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
  917.         boot_sec.bpb_media = boot_sec.bpb_media + 1;
  918.       } else {
  919.         printf("Error -- could not allocate new memory block from EMM.");
  920.         printf("  Error code %2x\n",_rax);
  921.         printf("Probably caused by not enough free memory.\n");
  922.         result = dose_arena;
  923.         break;
  924.       }
  925.     }
  926. #endif
  927.     if (expansion_type == nor_flg) {
  928.       _rax = dosf_alloc << 8;
  929.       _rbx = sec_per_blk * par_per_sec;
  930.       _doint(dosi_dosf);
  931.       if (_carryf == 1) {
  932.         printf("Error -- could not allocate new memory block.  Error code %1d\n",
  933.           _rax);
  934.         printf("         Probably caused by not enough free memory.\n");
  935.         result = dose_arena;
  936.         break;
  937.       } else {
  938.         boot_sec.mem_blk_table[boot_sec.bpb_media].typ = nor_flg;
  939.         boot_sec.mem_blk_table[boot_sec.bpb_media].par = _rax;
  940.         boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
  941.         boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = 0;
  942.         boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
  943.         boot_sec.bpb_media = boot_sec.bpb_media + 1;
  944.       }
  945.     }
  946. #if rm_support
  947.     /* --- Reserved memory must be initialized by zeroing it. --- */
  948.     if (expansion_type == rm_flg) {
  949.       for (j=0; j < sec_per_blk*bytes_per_sec; j++)
  950.          _poke(0x00, j, user_rm_addr);
  951.       boot_sec.mem_blk_table[boot_sec.bpb_media].typ = rm_flg;
  952.       boot_sec.mem_blk_table[boot_sec.bpb_media].par = user_rm_addr;
  953.       boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
  954.       boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = 0;
  955.       boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
  956.       boot_sec.bpb_media = boot_sec.bpb_media + 1;
  957.     }
  958. #endif
  959.   }
  960.   return(result);
  961.  
  962. } /* end of expand routine */
  963.  
  964. /*
  965.    ==== SHRINK ROUTINE ====
  966.         Shrinks the memory disk by moving all file sectors down
  967.         to lowest possible locations, then deallocating memory
  968.         blocks.
  969. */
  970.  
  971. int shrink(fat)
  972.   unsigned char fat[];
  973. {
  974.  
  975. /* ==== Data Storage ==== */
  976.  
  977. /* ---- Root Directory ---- */
  978.  
  979.   struct dir_entry root_dir[dir_per_sec];
  980.  
  981. /* ---- Other locals ---- */
  982.  
  983.   int cur_dir;                  /* ptr to current directory */
  984.   int dir_sector;               /* current directory sector */
  985.   int mdisk_dif;                /* desired change in sectors */
  986.   int done_dir;                 /* true when root directory completed */
  987.   char tmpstr[20];
  988.   int j, result;
  989.  
  990.   mdisk_dif = -mdisk_chg;       /* make variable positive */
  991.  
  992.   result = dose_noerr;
  993.  
  994.   /* ---- Calculate the location of the directory sectors. */
  995.  
  996.   first_dir_sector = boot_sec.bpb_reserved + 
  997.     (boot_sec.bpb_fat_size * boot_sec.bpb_fats);
  998.   last_dir_sector = (boot_sec.bpb_root * sizeof(struct dir_entry) /
  999.     bytes_per_sec) + first_dir_sector - 1;
  1000.  
  1001.   /* ---- First data sector follows the last root directory sector. */
  1002.  
  1003.   first_data_sector = last_dir_sector + 1;
  1004.  
  1005.   /*
  1006.      ---- Locate the first available cluster in the FAT. First FAT entry
  1007.           is number 2, not 0, because the first two FAT entries are used
  1008.           for the FAT ID and filler.
  1009.   */
  1010.  
  1011.   for (free_cluster = 2; fatget(free_cluster, fat) != available;
  1012.     free_cluster++);
  1013.  
  1014.   /*
  1015.      ---- Loop through the root directory, packing the clusters of
  1016.           each file to the lowest possible locations.
  1017.   */
  1018.  
  1019.   done_dir = false;
  1020.   for (dir_sector = first_dir_sector;
  1021.     !done_dir && dir_sector <= last_dir_sector;
  1022.     dir_sector++)
  1023.   {
  1024.     readsec(drive_number, dir_sector, &root_dir);
  1025.     for (cur_dir = 0; cur_dir < dir_per_sec; cur_dir++)
  1026.       if (root_dir[cur_dir].u_name.status == never_used) {
  1027.         done_dir = true;
  1028.         break;
  1029.       } else
  1030.       if ((result = pack_file(&(root_dir[cur_dir]), 0, fat)) != dose_noerr)
  1031.         break;
  1032.     writesec(drive_number, dir_sector, &root_dir);
  1033.     if (result != dose_noerr) break;
  1034.   }
  1035.  
  1036.   /*
  1037.       ---- Free up memory.  Loop backwards through the memory block table,
  1038.            freeing allocated memory blocks.  As we do so, decrement the
  1039.            media byte (memory block counter) and total sectors.
  1040.   */
  1041.  
  1042.   j = boot_sec.bpb_media - 1;
  1043.   if (result == dose_noerr)
  1044.     while (mdisk_dif >= boot_sec.mem_blk_table[j].siz) {
  1045.       if (boot_sec.mem_blk_table[j].typ == em_flg) {
  1046.         _rax = emm_fre_handle << 8;
  1047.         _rdx = boot_sec.mem_blk_table[j].hdl;
  1048.         _doint(usri_emm);
  1049.         _rax = _rax >> 8;
  1050.         if (_rax != 0) {
  1051.           printf ("Error -- could not free EM pages for memory block #%d.",j);
  1052.           printf ("         Error code %2x\n",_rax);
  1053.           result = dose_arena;
  1054.         }
  1055.       } else if (boot_sec.mem_blk_table[j].typ == nor_flg) {
  1056.         _res = boot_sec.mem_blk_table[j].par;
  1057.         _rax = dosf_dealloc << 8;
  1058.         _doint(dosi_dosf);
  1059.         if (_carryf == 1) {
  1060.           printf ("Error -- could not free allocated memory block #%d.",j);
  1061.           printf ("         Error code %d\n",_rax);
  1062.           result = dose_arena;
  1063.         }
  1064.       }
  1065.       boot_sec.bpb_media = boot_sec.bpb_media - 1;
  1066.       boot_sec.bpb_total = boot_sec.bpb_total - 
  1067.         boot_sec.mem_blk_table[j].siz;
  1068.       mdisk_dif = mdisk_dif - boot_sec.mem_blk_table[j].siz;
  1069.       j = j - 1;
  1070.     }
  1071.   return(result);
  1072.  
  1073. } /* end of shrink routine */
  1074.  
  1075. /*
  1076.    ==== PACK_FILE ROUTINE ====
  1077.         Given the directory entry for a file, packs all the clusters
  1078.         for that file to the lowest possible locations.  If the file
  1079.         is itself a subdirectory, then this routine will be called
  1080.         recursively from itself.
  1081.  
  1082.         Clusters are packed down by moving any cluster above
  1083.         "free_cluster" down to "free_cluster".  Then the next free
  1084.         cluster is located and the next cluster is examined.
  1085. */
  1086.  
  1087. int pack_file(dir, parent_cluster, fat)
  1088.   struct dir_entry *dir;
  1089.   int parent_cluster;
  1090.   unsigned char fat[];
  1091. {
  1092.   struct dir_entry a_sector[dir_per_sec];
  1093.   int next_cluster;
  1094.   int cur_cluster;
  1095.   int new_cluster;
  1096.   int result;
  1097.   int cur_dir;
  1098.   int result;
  1099.   int cluster_start;
  1100.   int done_dir;
  1101.   int j;
  1102.  
  1103.   /* Is this directory entry erased or never used?  If so, do nothing. */
  1104.  
  1105.   result = dose_noerr;
  1106.  
  1107. #if debug
  1108.   printf("Considering file %s for packing.\n",dir->u_name.name);
  1109. #endif
  1110.  
  1111.   if (dir->u_name.status != never_used && 
  1112.     dir->u_name.status != erased) {
  1113.  
  1114.     /* 
  1115.         If file is not 0 length or it is a subdirectory file,
  1116.         we can pack it.
  1117.     */
  1118.  
  1119.     if (dir->size > 0 || isdir(dir)) {
  1120.  
  1121. #if debug
  1122.       printf("  Packing file %s\n",dir->u_name.name);
  1123. #endif
  1124.     
  1125.       /* 
  1126.           Handle the first cluster number, which is stored in the
  1127.           directory entry, not in the FAT.
  1128.       */
  1129.  
  1130.       next_cluster = dir->first_cluster;
  1131.       if (next_cluster > free_cluster) {
  1132.         new_cluster = free_cluster;
  1133.         fatput(fatget(next_cluster, fat), new_cluster, fat);
  1134.         dir->first_cluster = new_cluster;
  1135.         fatput(available, next_cluster, fat);
  1136.         for (; fatget(free_cluster, fat) != available; free_cluster++);
  1137.         result = move_cluster(drive_number, next_cluster, new_cluster,
  1138.           &a_sector);
  1139.       }
  1140.  
  1141.       /* Handle rest of clusters stored in the FAT */
  1142.  
  1143.       if (result == dose_noerr) for (cur_cluster = dir->first_cluster;
  1144.         !islast(next_cluster = fatget(cur_cluster, fat));
  1145.         cur_cluster = fatget(cur_cluster, fat)) {
  1146.         if (next_cluster > free_cluster) {
  1147.           new_cluster = free_cluster;
  1148.           fatput(fatget(next_cluster, fat), new_cluster, fat);
  1149.           fatput(new_cluster, cur_cluster, fat);
  1150.           fatput(available, next_cluster, fat);
  1151.           for (; fatget(free_cluster, fat) != available; free_cluster++);
  1152.           result = move_cluster(drive_number, next_cluster, new_cluster,
  1153.             &a_sector);
  1154.           if (result != dose_noerr) break;
  1155.         }
  1156.       }
  1157.     }
  1158.  
  1159.     /*
  1160.         If the directory entry is for a subdirectory, then process
  1161.         it, packing each of its files.  Process a cluster at a time...
  1162.     */
  1163.  
  1164.     done_dir = false;
  1165.     if (isdir(dir)) for (cur_cluster = dir->first_cluster;
  1166.         !done_dir && !islast(cur_cluster);
  1167.         cur_cluster = fatget(cur_cluster, fat)) {
  1168.       if (result != dose_noerr) break;
  1169.       cluster_start = (cur_cluster-2) * boot_sec.sec_per_cluster +
  1170.         first_data_sector;
  1171.  
  1172.       /*  For each sector in the cluster... */
  1173.  
  1174.       for (j = 0; !done_dir && j < boot_sec.sec_per_cluster; j++) {
  1175.         result = readsec(drive_number, cluster_start + j, &a_sector);
  1176.         if (result != dose_noerr) break;
  1177.  
  1178.         /*
  1179.             For each directory entry in the sector, pack it.  The first
  1180.             two entries are special files ".." and "."
  1181.         */
  1182.  
  1183.         for (cur_dir = 0; cur_dir < dir_per_sec; cur_dir++) {
  1184.           if (a_sector[cur_dir].u_name.name[0] == '.') {
  1185.             if (a_sector[cur_dir].u_name.name[1] == '.')
  1186.               a_sector[cur_dir].first_cluster = parent_cluster;
  1187.             else a_sector[cur_dir].first_cluster = dir->first_cluster;
  1188.           }
  1189.           else if (a_sector[cur_dir].u_name.status == never_used) {
  1190.             done_dir = true;
  1191.             break;
  1192.           } else {
  1193.             /* In DeSmet C, local variables are allocated on the stack. */
  1194.             /* Make sure there is sufficient stack space for the recursive */
  1195.             /* call.  If not, display message, return error code, and */
  1196.             /* calling routines will attempt a graceful exit */
  1197.             if ((_showsp() - _memory()) > 700)
  1198.               result = pack_file(&(a_sector[cur_dir]), dir->first_cluster, fat);
  1199.             else {
  1200.               printf("Error -- Insufficient stack space to pack ");
  1201.               printf("subdirectory %s\n", a_sector[cur_dir].u_name.name);
  1202.               printf("Too many subdirectories.  Shrink abandoned. ");
  1203.               printf("Reboot recommended.\n");
  1204.               result = dose_too_many_dir;
  1205.               /* Note: Even though we can't handle the subdirectory, */
  1206.               /* keep going anyway so as to maintain integrity of disk. */
  1207.             }
  1208.           }
  1209.         }
  1210.         if (result != dose_noerr) break;
  1211.         writesec(drive_number, cluster_start + j, &a_sector);
  1212.       }
  1213.     }
  1214.   }
  1215.   if (result != dose_noerr)
  1216.     printf("Error -- Error while packing file %s\n",
  1217.       dir->u_name.name);
  1218.  
  1219. #if debug
  1220.   printf("Done packing file %s\n",dir->u_name.name);
  1221. #endif
  1222.  
  1223.   return(result);
  1224. }
  1225.  
  1226. /*
  1227.    ==== MOVE_CLUSTER ROUTINE ====
  1228.         Moves all sectors of specified cluster.
  1229. */
  1230.  
  1231. int move_cluster (drive, from, to, buffer)
  1232.   int drive, from, to;
  1233.   unsigned char *buffer;
  1234. {
  1235.   int result;
  1236.   int tmp_result;
  1237.   int j;
  1238.  
  1239.   from = (from-2) * boot_sec.sec_per_cluster + first_data_sector;
  1240.   to = (to-2) * boot_sec.sec_per_cluster + first_data_sector;
  1241.   for (j = 0; j < boot_sec.sec_per_cluster; j++) {
  1242.     tmp_result = readsec(drive, from + j, buffer);
  1243.     result = writesec(drive, to + j, buffer);
  1244.   }
  1245.   return(result + tmp_result);
  1246. }
  1247.  
  1248. /*
  1249.    ==== ISDIR ROUTINE ====
  1250.         Given a directory entry, returns true if the entry is for a
  1251.         subdirectory file.
  1252. */
  1253.  
  1254. int isdir(dir)
  1255.   struct dir_entry *dir;
  1256. {
  1257.   if (dir->attr & dir_bit) return(true); else return(false);
  1258. }
  1259.  
  1260. /*
  1261.    ==== ISLAST ROUTINE ====
  1262.         Given a FAT entry, returns true if it is an EOF marker.
  1263. */
  1264.  
  1265. int islast(cluster)
  1266.   int cluster;
  1267. {
  1268.   int j;
  1269.   int result;
  1270.  
  1271.   result = false;
  1272.   for (j = last_low; j <= last_high; j++)
  1273.     if (cluster == j) result = true;
  1274.   return(result);
  1275. }
  1276.  
  1277. /*
  1278.    ==== FATGET ROUTINE ====
  1279.         Given cluster number and FAT, returns the 12-bit FAT entry in
  1280.         and integer word, right-adjusted.
  1281. */
  1282.  
  1283. int fatget (cluster, fat)
  1284.   int cluster;
  1285.   unsigned char fat[];
  1286. {
  1287.   int clloc, clword;
  1288.  
  1289.   clloc = 3*cluster/2;
  1290.   clword = fat[clloc] + (fat[clloc+1] << 8);
  1291.   if (cluster & 1) return (clword >> 4); else return (clword & 0x0fff);
  1292. }
  1293.  
  1294. /*
  1295.    ==== FATPUT ROUTINE ====
  1296.         Given a 12-bit FAT entry, cluster number, and the FAT, stores
  1297.         the entry into the FAT.
  1298. */
  1299.  
  1300. int fatput (wd12bits, cluster, fat)
  1301.   int cluster;
  1302.   int wd12bits;
  1303.   unsigned char fat[];
  1304. {
  1305.   int clloc, clword;
  1306.  
  1307.   clloc = 3*cluster/2;
  1308.   clword = fat[clloc] + (fat[clloc+1] << 8);
  1309.   if (cluster & 1)
  1310.     clword = (clword & 0x000f) | (wd12bits << 4);
  1311.   else
  1312.     clword = (clword & 0xf000) | wd12bits;
  1313.   fat[clloc] = clword & 0x00ff;
  1314.   fat[clloc+1] = clword >> 8;
  1315. }
  1316.  
  1317. /*
  1318.    ==== DSP_MEM_BLK_TABLE ROUTINE ====
  1319.         Displays the Memory Block Table on the screen.
  1320. */
  1321.  
  1322. dsp_mem_blk_table()
  1323. {
  1324.   int j;
  1325.  
  1326.   printf("\n");
  1327. #if em_support
  1328.   printf("            Memory Block Table\n");
  1329.   printf("Block #   Paragraph     Size(K)    EM Handle\n");
  1330.   printf("-------   ---------    --------    ---------\n");
  1331.   for (j = 0; j < boot_sec.bpb_media; ++j) {
  1332.   printf("  %1d          %4x         %3d       ",
  1333.       j,boot_sec.mem_blk_table[j].par,
  1334.       boot_sec.mem_blk_table[j].siz/sec_per_K);
  1335.     if (boot_sec.mem_blk_table[j].typ == em_flg) 
  1336.       printf("%4x",boot_sec.mem_blk_table[j].hdl);
  1337.     else printf("  --");
  1338.     printf("\n");
  1339.   }
  1340. #else
  1341.   printf("      Memory Block Table\n");
  1342.   printf("Block #   Paragraph     Size(K)\n");
  1343.   printf("-------   ---------    --------\n");
  1344.   for (j = 0; j < boot_sec.bpb_media; ++j) {
  1345.   printf("  %1d         %4x          %3d\n",
  1346.       j,boot_sec.mem_blk_table[j].par,
  1347.       boot_sec.mem_blk_table[j].siz/sec_per_K);
  1348.   }
  1349. #endif
  1350.   printf("\n");
  1351.  
  1352. }
  1353.  
  1354. #if rm_support
  1355. /*
  1356.   ====  HTOI ROUTINE ====
  1357.         Convert hexadecimal string to integer.
  1358. */
  1359.  
  1360. int htoi(str)
  1361.   char str[];
  1362. {
  1363.   int temp;
  1364.   int j;
  1365.   int nibble;
  1366.  
  1367.   temp = 0;
  1368.   for (j=0; ishex(str[j]); j++) {
  1369.     nibble = toupper(str[j]) - '0';
  1370.     if (nibble > 9) nibble = nibble - ('A'-'9'-1);
  1371.     if (nibble < 0 || nibble > 15) break;
  1372.     temp = (temp << 4) + nibble;
  1373.   }
  1374.   return(temp);
  1375. }
  1376.  
  1377. /*
  1378.    ==== ISHEX ROUTINE ====
  1379.         Returns true if character is hexadecimal (upper or lower case).
  1380. */
  1381.  
  1382. int ishex(ch)
  1383.   char ch;
  1384. {
  1385.   if (isdigit(ch) || (toupper(ch) >='A' && toupper(ch) <= 'F'))
  1386.     return(true);
  1387.   else return(false);
  1388. }
  1389. #endif
  1390.  
  1391. /*
  1392.    ==== PEEKW ROUTINE ====
  1393.         Given segment and offset, returns the word thereby addressed.
  1394.         Written because DeSmet C uses small memory model and there are
  1395.         no FAR data types.
  1396. */
  1397.  
  1398. int peekw(off, seg)
  1399.   unsigned char *off;
  1400.   int seg;
  1401. {
  1402.   return((_peek(off+1, seg) << 8) + _peek(off, seg));
  1403. }
  1404.  
  1405. /*
  1406.    ==== READSEC ====
  1407.         Given drive number, desired logical sector number, and a sector
  1408.         buffer, this routine reads the sector into the buffer.
  1409.  
  1410.   NOTE: This routine uses DeSmet's "_doint" routine to perform absolute
  1411.         disk read interrupt number 25H.  This interrupt, unlike most
  1412.         interrupts, leaves the flags register on the stack when it returns.
  1413.         In DeSmet this is OK, because the called routine restores the
  1414.         stack frame prior to RETurning.  In some C compilers, this scheme
  1415.         won't work.  For example, in AZTEC C, it is the CALLING routine's
  1416.         responsibility to restore the stack frame.  In AZTEC, then, we
  1417.         would RETurn to the wrong address and die.  So look out, if you are
  1418.         converting this program to some other version of C.
  1419. */
  1420.  
  1421. int readsec (drive, sector, buffer)
  1422.   int drive;
  1423.   int sector;
  1424.   char *buffer;
  1425. {
  1426.   _rax = drive;
  1427.   _rds = _showds();  /* ASSUMPTION: DS = SS in DeSmet C */
  1428.   _rbx = buffer;
  1429.   _rcx = 1;
  1430.   _rdx = sector;
  1431.   _doint(dosi_dsk_read);
  1432.   if (_carryf == 1) return (_rax); else return (dose_noerr);
  1433. }
  1434.  
  1435. /*
  1436.    ==== WRITESEC ROUTINE ====
  1437.         Given drive number, logical sector number, and a buffer, this
  1438.         routine writes the sector to disk.
  1439.  
  1440.         See the note under routine READSEC.
  1441. */
  1442.  
  1443. int writesec (drive, sector, buffer)
  1444.   int drive;
  1445.   int sector;
  1446.   char *buffer;
  1447. {
  1448.   _rax = drive;
  1449.   _rds = _showds();     /* ASSUMPTION: DS = SS in DeSmet C */
  1450.   _rbx = buffer;
  1451.   _rcx = 1;
  1452.   _rdx = sector;
  1453.   _doint(dosi_dsk_write);
  1454.   if (_carryf == 1) return (_rax); else return (dose_noerr);
  1455. }
  1456. /*
  1457.    ==== ADJRAM_EXIT ROUTINE ====
  1458.         Displays exit code and exits with that code to DOS.
  1459. */
  1460. int adjram_exit(err_code)
  1461.   int err_code;
  1462. {
  1463.   printf("ADJRAM exiting (code %d)\n",err_code);
  1464.   exit(err_code);
  1465. }
  1466.  
  1467. /*
  1468.    ==== DSP_USAGE ROUTINE ====
  1469.         Displays message how to use program.
  1470. */
  1471.  
  1472. int dsp_usage()
  1473.  
  1474. {
  1475.  printf("WARNING: This program is distributed with documentation and ");
  1476.  printf("source code.\n");
  1477.  printf("         Using it without first reading the documentation ");
  1478.  printf("is hazardous.\n");
  1479.  printf("Usage:  ADJRAM drive size option\nWhere:\n");
  1480.  printf("  drive = RAM disk drive letter, A to L\n");
  1481.  printf("  size  = size of RAM disk in K (minimum %d) in following format:\n",
  1482.    min_size_K);
  1483.  printf("            xxx   = set RAM disk size to xxx\n");
  1484.  printf("            +xxx  = increase RAM disk size by xxx\n");
  1485.  printf("            -xxx  = decrease RAM disk size by xxx\n");
  1486.  printf("            Fxxx  = adjust size so there is xxx free space\n");
  1487.  printf("            Mxxx  = ensure there is at least xxx of free space\n");
  1488.  printf("          Size will be rounded to the next highest %dK\n",
  1489.    K_per_blk);
  1490.  printf("          If size is omitted, current drive size is displayed.\n");
  1491.  printf("  option= optional /T to display Memory Block Table\n");
  1492. #if em_support
  1493.  printf("          optional /E to expand using LIM Expanded Memory\n");
  1494. #endif
  1495.  printf("Examples:\n");
  1496.  printf("  ADJRAM C: 250\n");
  1497.  printf("          will set RAM disk C to 256K\n");
  1498. #if em_support
  1499.  printf("  ADJRAM i: +250 /e /t\n");
  1500.  printf("          will increase RAM disk I by 256K using Expanded Memory\n");
  1501. #else
  1502.  printf("  ADJRAM i: +60 /t\n");
  1503.  printf("          will increase RAM disk I by 64K\n");
  1504. #endif
  1505.  printf("          and will display the Memory Block Table\n");
  1506.  printf("  ADJRAM C: m64\n");
  1507.  printf("          will ensure there is at least 64K free space on drive C\n");
  1508. }
  1509.